#include "finderpattern.h"

finderpattern::finderpattern()
{

}

void finderpattern::findFinders(QImage* img)
{
    vector<point> pointList = checkHorizontalUpLeft(0, img->height()-1, img);
    upperLeftFinder = selectBestFinder(pointList);

    pointList = checkHorizontalDownLeft(img->height()-1, 0, img);
    lowerLeftFinder = selectBestFinder(pointList);

    pointList = checkHorizontalUpRight(0, img->height()-1, img);
    upperRightFinder = selectBestFinder(pointList);

    calculateAngle();
}

void finderpattern::calculateAngle()
{
    int y = upperLeftFinder.getRow()-upperRightFinder.getRow();
    int x = upperLeftFinder.getColumn()-upperRightFinder.getColumn();
    if (x != 0)
    {
        angle = atan(abs(y)/abs(x));
        if (angle != 0)
            angle *= (x > 0)? 1 : -1;
    }
}

point finderpattern::selectBestFinder(vector<point> pointList)
{
    point pf;
    if (pointList.size() == 0)
        return pf;
    int total = 0;
    for (int i = 0; i < pointList.size(); i++)
    {
        point pAux = pointList.at(i);
        total += pAux.getRow();
    }
    int average = total/pointList.size();
    point p = pointList.at(0);
    pf.setPoint(average,p.getColumn());
    return pf;
}

vector<point> finderpattern::checkHorizontalUpLeft(int rowStart, int rowEnd, QImage *img)
{
    vector<point> pointList;

    int stateCount[5] = {0};
    int currentState = 0;
    bool found = false;
    bool over = false;
    int offset;

    pixel p;

    for(int row = rowStart; row < rowEnd; row++)
    {
        if (over)
            break;

        stateCount[0] = 0;
        stateCount[1] = 0;
        stateCount[2] = 0;
        stateCount[3] = 0;
        stateCount[4] = 0;
        currentState = 0;

        offset = 0;

        for (int col = 0; col < img->width(); col++)
        {
            if(p.isPixelBlack(row, col, img))
            {
                if((currentState & 0x1)==1)
                    currentState++;
                stateCount[currentState]++;
            }
            else
            {
                if((currentState & 0x1)==1)
                    stateCount[currentState]++;
                else
                {
                    if(currentState==4)
                    {
                        if(checkRatio(stateCount))
                        {
                            int total = 0;
                            for (int i = 0; i < 5; i++)
                                total += stateCount[i];
                            if (crossCheckVertical(row - total/2, col - total/2, img))
                            {
                                int middle = (2*col-total)/2;
                                point p;
                                p.setPoint(row, middle);
                                pointList.push_back(p);
                                found = true;
                                break;
                            }
                        }
                        else
                        {
                            currentState  = 3;
                            stateCount[0] = stateCount[2];
                            stateCount[1] = stateCount[3];
                            stateCount[2] = stateCount[4];
                            stateCount[3] = 1;
                            stateCount[4] = 0;

                            offset++;
                            if (found & (offset > 1))
                            {
                                over = true;
                                break;
                            }
                            continue;
                        }
                        currentState  = 0;
                        stateCount[0] = 0;
                        stateCount[1] = 0;
                        stateCount[2] = 0;
                        stateCount[3] = 0;
                        stateCount[4] = 0;
                    }
                    else
                    {
                        currentState++;
                        stateCount[currentState]++;
                    }
                }
            }
        }
    }

    return pointList;
}

vector<point> finderpattern::checkHorizontalDownLeft(int rowStart, int rowEnd, QImage *img)
{
    vector<point> pointList;

    int stateCount[5] = {0};
    int currentState = 0;
    bool found = false;
    bool over = false;
    int offset;

    pixel p;

    for(int row = rowStart; row > rowEnd; row--)
    {
        if (over)
            break;

        stateCount[0] = 0;
        stateCount[1] = 0;
        stateCount[2] = 0;
        stateCount[3] = 0;
        stateCount[4] = 0;
        currentState = 0;

        offset = 0;

        for (int col = 0; col < img->width(); col++)
        {
            if(p.isPixelBlack(row, col, img))
            {
                if((currentState & 0x1)==1)
                    currentState++;
                stateCount[currentState]++;
            }
            else
            {
                if((currentState & 0x1)==1)
                    stateCount[currentState]++;
                else
                {
                    if(currentState==4)
                    {
                        if(checkRatio(stateCount))
                        {
                            int total = 0;
                            for (int i = 0; i < 5; i++)
                                total += stateCount[i];
                            if (crossCheckVertical(row - total/2, col - total/2, img))
                            {
                                int middle = (2*col-total)/2;
                                point p;
                                p.setPoint(row, middle);
                                pointList.push_back(p);
                                found = true;
                                break;
                            }
                        }
                        else
                        {
                            currentState  = 3;
                            stateCount[0] = stateCount[2];
                            stateCount[1] = stateCount[3];
                            stateCount[2] = stateCount[4];
                            stateCount[3] = 1;
                            stateCount[4] = 0;

                            offset++;
                            if (found & (offset > 1))
                            {
                                over = true;
                                break;
                            }
                            continue;
                        }
                        currentState  = 0;
                        stateCount[0] = 0;
                        stateCount[1] = 0;
                        stateCount[2] = 0;
                        stateCount[3] = 0;
                        stateCount[4] = 0;
                    }
                    else
                    {
                        currentState++;
                        stateCount[currentState]++;
                    }
                }
            }
        }
    }

    return pointList;
}

vector<point> finderpattern::checkHorizontalUpRight(int rowStart, int rowEnd, QImage *img)
{
    vector<point> pointList;

    int stateCount[5] = {0};
    int currentState = 0;
    bool found = false;
    bool over = false;
    int offset;

    pixel p;

    for(int row = rowStart; row < rowEnd; row++)
    {
        if (over)
            break;

        stateCount[0] = 0;
        stateCount[1] = 0;
        stateCount[2] = 0;
        stateCount[3] = 0;
        stateCount[4] = 0;
        currentState = 0;

        offset = 0;

        for (int col = img->width()-1; col > 0; col--)
        {
            if(p.isPixelBlack(row, col, img))
            {
                if((currentState & 0x1)==1)
                    currentState++;
                stateCount[currentState]++;
            }
            else
            {
                if((currentState & 0x1)==1)
                    stateCount[currentState]++;
                else
                {
                    if(currentState==4)
                    {
                        if(checkRatio(stateCount))
                        {
                            int total = 0;
                            for (int i = 0; i < 5; i++)
                                total += stateCount[i];
                            if (crossCheckVertical(row - total/2, col + total/2, img))
                            {
                                int middle = (2*col+total)/2;
                                point p;
                                p.setPoint(row, middle+1);
                                pointList.push_back(p);
                                found = true;
                                break;
                            }
                        }
                        else
                        {
                            currentState  = 3;
                            stateCount[0] = stateCount[2];
                            stateCount[1] = stateCount[3];
                            stateCount[2] = stateCount[4];
                            stateCount[3] = 1;
                            stateCount[4] = 0;

                            offset++;
                            if (found & (offset > 1))
                            {
                                over = true;
                                break;
                            }
                            continue;
                        }
                        currentState  = 0;
                        stateCount[0] = 0;
                        stateCount[1] = 0;
                        stateCount[2] = 0;
                        stateCount[3] = 0;
                        stateCount[4] = 0;
                    }
                    else
                    {
                        currentState++;
                        stateCount[currentState]++;
                    }
                }
            }
        }
    }

    return pointList;
}

bool finderpattern::crossCheckVertical(int startRow, int startCol, QImage* img)
{
    int currentState = 0;
    int stateCount[5] = {0};

    pixel p;

    for (int row = 0; row <= startCol*2; row++)
    {
        if (row + startRow > img->height()-1 || row + startRow < 0) {
            return false;
        }

        if(p.isPixelBlack(row + startRow, startCol, img))
        {
            if((currentState & 0x1)==1)
                currentState++;
            stateCount[currentState]++;
        }
        else
        {
            if((currentState & 0x1)==1)
                stateCount[currentState]++;
            else
            {
                if(currentState==4)
                {
                    if(checkRatio(stateCount))
                        return true;
                }
                else
                {
                    currentState++;
                    stateCount[currentState]++;
                }
            }
        }
    }
    return false;
}

bool finderpattern::checkRatio(int stateCount[])
{
    int totalFinderSize = 0;
    for(int i=0; i<5; i++)
    {
        int count = stateCount[i];
        totalFinderSize += count;
        if(count==0)
            return false;
    }
    if(totalFinderSize<7)
        return false;

    int moduleSize = ceil(totalFinderSize / 7.0);
    int maxVariance = moduleSize/2;
    bool retVal= ((abs(moduleSize - (stateCount[0])) < maxVariance) &&
        (abs(moduleSize - (stateCount[1])) < maxVariance) &&
        (abs(3*moduleSize - (stateCount[2])) < 3*maxVariance) &&
        (abs(moduleSize - (stateCount[3])) < maxVariance) &&
        (abs(moduleSize - (stateCount[4])) < maxVariance));
    return retVal;
}

void finderpattern::paintFinders(int pixelProportion, QImage *img)
{
    pixel p;
    for (int row = 0; row < 8*pixelProportion; row += pixelProportion)
    {
        for (int col = 0; col < 8*pixelProportion; col += pixelProportion)
        {
            p.paintPixel(getUpperLeftFinder().getRow()-3*pixelProportion+row, getUpperLeftFinder().getColumn()-3*pixelProportion+col, pixelProportion, qRgb(0,255,0), img);
            p.paintPixel(getUpperRightFinder().getRow()-3*pixelProportion+row, getUpperRightFinder().getColumn()-4*pixelProportion+col, pixelProportion, qRgb(0,255,0), img);
            p.paintPixel(getLowerLeftFinder().getRow()-4*pixelProportion+row, getLowerLeftFinder().getColumn()-3*pixelProportion+col, pixelProportion, qRgb(0,255,0), img);
        }
    }

    converter c;
    log->addLog("\nFINDER SUPERIOR ESQUERDO = (" + c.intToString(getUpperLeftFinder().getRow()) + "," + c.intToString(getUpperLeftFinder().getColumn()) + ")");
    log->addLog("\nFINDER SUPERIOR DIREITO = (" + c.intToString(getUpperRightFinder().getRow()) + "," + c.intToString(getUpperRightFinder().getColumn()) + ")");
    log->addLog("\nFINDER INFERIOR ESQUERDO = (" + c.intToString(getLowerLeftFinder().getRow()) + "," + c.intToString(getLowerLeftFinder().getColumn()) + ")");
}

void finderpattern::setCanvas(canvas *log)
{
    this->log = log;
}

point finderpattern::getUpperLeftFinder()
{
    return this->upperLeftFinder;
}

point finderpattern::getUpperRightFinder()
{
    return this->upperRightFinder;
}

point finderpattern::getLowerLeftFinder()
{
    return this->lowerLeftFinder;
}

float finderpattern::getAngle()
{
    return this->angle;
}
